//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================




#include <ddk.h>
#include "dm9000.h"
#include "dm9000var.h"


static int     DM9000Initialize(PDM9000 pDm9000Dev);
void DM9000Isr(irq_t irq, void *pvDevice, InterruptContext *pContext);
ECode DM9000Interrupt(void *arg);
static void    DM9000Watchdog(PDM9000 pDm9000Dev);
EXTERN int Sleep(unsigned int uTicks);
extern int lwipentry();
EXTERN Thread *CreateSystemThread(
        threadroutine_t threadroutine, void *pvParameter,
        ScheduleClass *pScheduleClass, uint_t uSchedulePolicy);
/*
* -------------------------------------------------------------
* Common device driver handling loop
*/

SystemTime tv1,tv2,tv3,tv4,tv5,tv6;
ECode DeviceThread(void *arg)
{
    PDM9000 pThis = (PDM9000)arg;

    /* BUGBUG - need appropriate RTL function
    * for now hack mips version to use physical address
    */

    DM9000Initialize(pThis);
    for (;;) {
        WaitResult wr;
        pThis->pISRevent->Wait(&wr, NULL);
//        DzGetSystemTime(&tv2);
//        kprintf("tv1:%08x_%08x tv2:%08x_%08x\n", tv1.seconds, tv1.microseconds,
//                        tv2.seconds, tv2.microseconds);
        DM9000Interrupt(pThis);
    }
}


ECode WatchDogThread(void *arg)
{
    PDM9000 pThis = (PDM9000)arg;

    /* BUGBUG - need appropriate RTL function
    * for now hack mips version to use physical address
    */
    for (;;) {
        DM9000Watchdog(pThis);
        DzSleep(10000000 * 2, NULL);
    }
    return NOERROR;
}

unsigned int SystemTime()
{
    SystemTime time;
    DzGetSystemTime(&time);
    return time.seconds*1000*1000*10 + time.microseconds*1000*10;
}


//#define DM9000_DEBUG

#if defined(DM9000_DEBUG)
#define DM9000DBUG(dbug_now, msg, vaule) \
     if (dbug_now) kprintf("DM9000: %s %x\n", msg, vaule)
#else
#define DM9000DBUG(dbug_now, msg, vaule)
#endif

static UInt8 nfloor = 0;
static UInt8 macAddr[6] = {0x0, 0x1, 0x2, 0x3, 0x02, 0x1e};
unsigned int address1, address2;
void Dm_Delay(UInt32  ms);


Node g_nodereadlist;
Node g_nodewritelist;
PDM9000 pdm9000;
EXTERN_C Boolean __stdcall dm9000new()
{
    PDM9000   pDm9000Dev;
    struct board_info *db; /* Point a board information structure */
    UInt32 id_val = 0, i;
    UInt32 Irq = 0x5;


    address1 = (unsigned int)DzKIoRemap(0x20000000, 4*1024);
    address2 = (unsigned int)DzKIoRemap(0x60000000, 4*1024);

    DM9000DBUG(1, "DM9000probe()",0);

    CPLD_Init();

    /* Search All DM9000 NIC */
    IOSpaceWriteInt8( BASEADDR, DM9000_VID_L);
    Dm_Delay(STDTIME);
    id_val = IOSpaceReadInt8(BASEADDR + 4);
    IOSpaceWriteInt8( BASEADDR, DM9000_VID_H);
    Dm_Delay(STDTIME);
    id_val |= IOSpaceReadInt8(BASEADDR + 4) << 8;
    IOSpaceWriteInt8( BASEADDR, DM9000_PID_L);
    Dm_Delay(STDTIME);
    id_val |= IOSpaceReadInt8(BASEADDR + 4) << 16;
    IOSpaceWriteInt8( BASEADDR, DM9000_PID_H);
    Dm_Delay(STDTIME);
    id_val |= IOSpaceReadInt8(BASEADDR + 4) << 24;
    kprintf("<DM9000> I/O: %x, VID: %x \n",BASEADDR, id_val);

    if (id_val == DM9000_ID) {
        pDm9000Dev = (PDM9000)(malloc(sizeof(*pDm9000Dev)));
        if (pDm9000Dev == NULL) {
            return FALSE;
        }
        pdm9000 = pDm9000Dev;
        memset(pDm9000Dev, 0, sizeof(*pDm9000Dev));

        /* Allocated board information structure */
        db = (struct board_info *)(malloc(sizeof(*db)));
        if (db == NULL) {
            free(pDm9000Dev);
            return FALSE;
        }
        memset(db, 0, sizeof(*db));
        pDm9000Dev->board   = db;   /* link device and board info */
        db->ioaddr = BASEADDR;
        db->io_data = BASEADDR + 4;
        db->irq = Irq;

        /* Read SROM content */
        for (i=0; i<64; i++) {
            ((UInt16 *)db->srom)[i] = read_srom_word(db, i);
            kprintf("%04x ", ((UInt16 *)db->srom)[i]);
        }

        /* Set Node Address */
        kprintf(" mac addr=");
        for (i=0; i<6; i++) {
            pDm9000Dev->dev_addr[i] = db->srom[i];
            pDm9000Dev->dev_addr[i] = macAddr[i];
            kprintf("%02x ", pDm9000Dev->dev_addr[i]);
        }
        kprintf("\n");
    }
    else {
        kprintf("not Found Dm9000 network card\n");
        return 0;
    }
//registry irq;
    DzRegisterIsr(db->irq, IPL10, DM9000Isr, pDm9000Dev);
    g_nodereadlist.Initialize();
    g_nodewritelist.Initialize();

    pDm9000Dev->pRcondition = new DzCondition();
    pDm9000Dev->pWcondition = new DzCondition();
    pDm9000Dev->pRmutex = new DzMutex();
    pDm9000Dev->pWmutex = new DzMutex();

    lwipentry();
    return  TRUE ;
}


int
dm9000_connect(void * softc, void** hardc)
{
    pdm9000->pISRevent = new DzEvent;

    //copy mac address
    *hardc = &(pdm9000->dev_addr[0]);

//    ECode ec = DzCreateKernelThread(DeviceThread, (void*)pdm9000, 0
//            , ThreadPriorityRank_Normal, NULL);
//    if (FAILED(ec)) {
//        kprintf("*ERROR* failed to create kernelthread\n");
//    }
//
//    ec = DzCreateKernelThread(WatchDogThread, (void*)pdm9000, 0
//            , ThreadPriorityRank_Normal, NULL);
//    if (FAILED(ec)) {
//        kprintf("*ERROR* failed to create kernelthread\n");
//    }
    Thread *pThread = CreateSystemThread((threadroutine_t)DeviceThread, (void*)pdm9000,
        g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    if (!pThread) {
        kprintf("*ERROR* failed to create systemthread\n");
    }

    pThread = CreateSystemThread((threadroutine_t)WatchDogThread, (void*)pdm9000,
        g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    if (!pThread) {
        kprintf("*ERROR* failed to create systemthread\n");
    }
    return 0;
}


static int
DM9000Initialize(PDM9000 pDm9000Dev)
{
    board_info_t * db = (board_info_t *)pDm9000Dev->board;

    DM9000DBUG(1, "DM9000Initalize", 0);

    /* Initilize DM910X board */
    PDM9000_Init (pDm9000Dev);

    /* Init driver variable */
    db->dbug_cnt = 0;
    db->runt_length_counter = 0;
    db->long_length_counter = 0;
    db->reset_counter = 0;

    return 0;
}


/* Initilize DM9000 board
*/
void PDM9000_Init  (DM9000 *pDm9000Dev)
{
    board_info_t *db = (board_info_t *)pDm9000Dev->board;

    DM9000DBUG(1, "PDM9000_Init ()", 0);

    iow( db, DR_GP_CONTROL, 0x01 );           //active the internal PHY
    iow( db, DR_GP_REGISTER, 0x00 );

    /* RESET device */
    iow(db, 0, 1);
    Dm_Delay(1);  /* delay 100us */
    while( ( ior(db, 0x00 ) & 0x01 ) );       //wait for software reset

    iow(db, DR_GP_REGISTER, 0x01 );
    Dm_Delay(2 );
    iow(db, DR_GP_REGISTER, 0x00 );

    iow(db, 0x0, 0x00 );                     //normal mode
    ior(db, 0x1 );                           //Clear TX status

    /* I/O mode */
    db->io_mode = ior(db, 0xfe) >> 6; /* ISR bit7:6 keeps I/O mode */
     identify_nic(db);

    /* GPIO0 on pre-activate PHY */
    iow(db,0x1f,0x00); /*REG_1F bit0 activate phyxcer*/

    /* Set PHY */
    db->op_mode = DM9000_AUTO;
    set_PHY_mode(db);

    /* Init needed register value */
    db->reg0 = DM9000_REG00;
    if ( (db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_1M_HPNA) )
        db->reg0 |= DM9000_EXT_MII;

    /* User passed argument */
    db->reg5 = DM9000_REG05;
    db->reg8 = DM9000_REG08;
    db->reg9 = DM9000_REG09;
    db->rega = DM9000_REG0A;

    /* Program operating register */
    iow(db, 0x00, db->reg0);
    iow(db, 0x02, 0);         /* TX Polling clear */
    iow(db, 0x08, 0x37);      /* Less 3Kb, 200us */
    iow(db, 0x09, db->reg9);  /* Flow Control : High/Low Water */
    iow(db, 0x0a, db->rega);  /* Flow Control */
    iow(db, 0x2f, 0);         /* Special Mode */
    iow(db, 0x01, 0x2c);      /* clear TX status */
    iow(db, 0xfe, 0x0f);      /* Clear interrupt status */

    DM900_SetMac( pDm9000Dev);

    /* Set address filter table */
    DM900_MultCast(pDm9000Dev);

    /* Activate DM9000 */
    iow(db, 0x05, db->reg5 | 1);/* RX enable */

    iow(db, 0xff, DM9000_REGFF);/* Enable TX/RX interrupt mask */

    /* Init Driver variable */
    db->link_failed = 1;
    db->tx_pkt_cnt = 0;
    db->queue_pkt_len = 0;
    pDm9000Dev->trans_start = 0;

    CPLD_SetIntMask( ETH_INT_MASK, 1 );  // enable dm9000 interrupt
}


#define RL_MIN_FRAMELEN      60

/*
 * Hardware start transmission.
 * Send a packet to media from the upper layer.
 */
Int32
DM9000Xmit(PDM9000    pDm9000Dev)
{
    board_info_t *db = (board_info_t *)pDm9000Dev->board;
    int i, datalen;
    char *data_ptr;
    PDM9000 pThis = pDm9000Dev;

    DM9000DBUG(1, "DM9000start_xmit", 0);

    if (db->tx_pkt_cnt > 1) {
        return 1;
    }

    /* Disable all interrupt */
    iow(db, 0xff, 0x80);

    {
//        pThis->netp->hwflags = 0;
//        ;
//    }
//    else {
//        QUEUE_OUT(pThis->netp, depart, mess);
        /* Check to see if we really want to transmit
           this frame.  It is possible for USNET to
           mark a frame in the depart queue as cancelled
           by changing the value of mess->offset to a
           value other than netno.  For example, if a
           frame times out waiting for an ARP reply,
           NetTask() changes offset to boTXDONE and
           releases the message buffer back into the
           pool.  If a frame has been cancelled, go on to
           the next frame in the depart queue.  */
        /* Set up the frame for transmission.  The frame
           starts at (char *)mess + MESSH_SZ and is len
           bytes long.  Note that some ethernet controllers
           will automatically pad out frames that are
           shorter than the minimum ethernet frame size,
           others may not, and so len may need to be
           adjusted.  */

        WaitResult wr;
        pThis->pWmutex->Lock(&wr);
        if(g_nodewritelist.IsEmpty()){
            pThis->pWmutex->Unlock();
            return 0;
        }
        Node * pnode = g_nodewritelist.GetNode();
        pThis->pWmutex->Unlock();
        data_ptr = (char*)pnode->m_address;
        datalen = pnode->m_len;

    //dump_buf(data_ptr,datalen, 0, 1);

        IOSpaceWriteInt8(db->ioaddr, 0xf8);

        if (db->io_mode == DM9000_BYTE_MODE) { /* Byte mode */
            for (i = 0; i <datalen ; i++)
                IOSpaceWriteInt8( db->io_data, (data_ptr[i] & 0xff));
        }
        else if (db->io_mode == DM9000_WORD_MODE) { /* Word mode */
            for (i = 0; i < (datalen + 1) / 2; i++)
                IOSpaceWriteInt16( db->io_data, ((UInt16 *)data_ptr)[i]);
        }
        else { /* DWord mode */
            for (i = 0; i< (datalen + 3) / 4; i++) {
                IOSpaceWriteInt32(db->io_data, ((UInt32 *)data_ptr)[i]);
//                _CThread_Delay(1);
            }
//        DzGetSystemTime(&tv5);
//    kprintf("drv write %d\n", datalen);
    for(int ii = 0; ii < datalen; ii++){
//        kprintf("%02x ", *(data_ptr+ii));
        for(int ci = 0; ci < 800; ci++);//1000
    }
//    kprintf("\n");
//        DzGetSystemTime(&tv6);
////        kprintf("tv5:%08x_%08x tv6:%08x_%08x\n", tv5.seconds, tv5.microseconds,
////                        tv6.seconds, tv6.microseconds);

        }

        /* TX control: First packet immediately send, second packet queue */
        if (db->tx_pkt_cnt == 0) {
            /* First Packet */
            db->tx_pkt_cnt++;

            /* Set TX length to DM9000 */
            iow(db, 0xfc, datalen & 0xff);
            iow(db, 0xfd, (datalen >> 8) & 0xff);

            /* Issue TX polling command */
            iow(db, 0x2, 0x1);/* Cleared after TX complete */

            pDm9000Dev->trans_start = SystemTime();/*saved the time stamp*/

        } else {
            /* Second packet */
            db->tx_pkt_cnt++;
            db->queue_pkt_len = datalen;
        }

        free(pnode->m_address);
        delete pnode;

        iow(db,0xff,0x83);
    }

    return 0;
}


/*
Stop the interface.
The interface is stopped when it is brought.
*/
int DM9000stop(PDM9000  pDm9000Dev)
{
    board_info_t *db = (board_info_t *)pDm9000Dev->board;

    DM9000DBUG(1, "DM9000stop", 0);
    CPLD_SetIntMask( ETH_INT_MASK, 0 );

    /* RESET devie */
    phy_write(db, 0x00, 0x8000);        /* PHY RESET */
    iow(db, 0x1f, 0x01);                /* Power-Down PHY */
    iow(db, 0xff, 0x80);                /* Disable all interrupt */
    iow(db, 0x05, 0x00);                /* Disable RX */

    /* Dump Statistic counter */
#if TRUE
    kprintf("\nRX FIFO OVERFLOW %lx\n", db->stats.rx_fifo_errors);
    kprintf("RX CRC %lx\n", db->stats.rx_crc_errors);
    kprintf("RX LEN Err %lx\n", db->stats.rx_length_errors);
    kprintf("RX LEN < 64byte %x\n", db->runt_length_counter);
    kprintf("RX LEN > 1514byte %x\n", db->long_length_counter);
    kprintf("RESET %x\n", db->reset_counter);
    kprintf("RESET: TX Timeout %x\n", db->reset_tx_timeout);
    kprintf("RESET: RX Status Wrong %x\n", db->reset_rx_status);
#endif

    return 0;
}

/*
DM9102 insterrupt handler
receive the packet to upper layer, free the transmitted packet
*/
void DM9000tx_done(PDM9000  pDm9000Dev, board_info_t *db)
{

    int tx_status = ior(db, 0x01);        /* Got TX status */

    if (tx_status & 0xc) {
        /* One packet sent complete */
        db->tx_pkt_cnt--;
        pDm9000Dev->trans_start = 0;
        db->stats.tx_packets++;

        /* Queue packet check & send */
        if (db->tx_pkt_cnt > 0) {
            iow(db, 0xfc, db->queue_pkt_len & 0xff);
            iow(db, 0xfd, (db->queue_pkt_len >> 8) & 0xff);
            iow(db, 0x2, 0x1);
            pDm9000Dev->trans_start = SystemTime();
        }
    }
}


void __cdecl DM9000Isr(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    PDM9000 pDm9000Dev = (PDM9000)pvDevice;
    board_info_t *db = (board_info_t *)pDm9000Dev->board;

    DM9000DBUG(1, "DM9000Isr", 0);
    /* Save previous register address */
    pDm9000Dev->reg_save = IOSpaceReadInt8(db->ioaddr);

    /* Disable all interrupt */
    iow(db, 0xff, 0x80);

	/* Wakeup handler thread and we are done. */
    pDm9000Dev->pISRevent->NotifyByIsr();
    return;
}


ECode DM9000Interrupt(void *arg)
{
    PDM9000 pDm9000Dev = (PDM9000)arg;
    board_info_t *db = pDm9000Dev->board;
    int int_status;

    /* Got DM9000 interrupt status */
    int_status = ior(db, 0xfe);/* Got ISR */
    iow(db, 0xfe, int_status);/* Clear ISR status */

    /* Trnasmit Interrupt check */
    if (int_status & DM9000_TX_INTR) {//0x2
        DM9000tx_done(pDm9000Dev,db);
    }

    /* Received the coming packet */
    if (int_status & DM9000_RX_INTR) {//0x1
        DM9000Receive(pDm9000Dev);
    }

    if (!g_nodewritelist.IsEmpty())  {
        /* Data in Tx buffer waiting for transimission */
        DM9000Xmit(pDm9000Dev);
    }

    /* Activate DM9000 */
    iow(db, 0x05, db->reg5 | 5);/* RX enable */
    iow(db, 0xff, DM9000_REGFF);/* Enable TX/RX interrupt mask */

    /* Restore previous register address */
    IOSpaceWriteInt8(db->ioaddr, pDm9000Dev->reg_save);
    return 1;
}


/*
A periodic timer routine
Dynamic media sense, allocated Rx buffer...
*/
static void    DM9000Watchdog(PDM9000 pDm9000Dev)
{
    board_info_t *db = (board_info_t *)pDm9000Dev->board;
    UInt8 reg_save, tmp_reg;

    DM9000DBUG(0, "DM9000timer()", 0);

    /* Save previous register address */
    reg_save = IOSpaceReadInt8(db->ioaddr);

    /* TX timeout check */
    if (pDm9000Dev->trans_start&&((SystemTime()-pDm9000Dev->trans_start)
                                  >DM9000TX_TIMEOUT)) {
        db->device_wait_reset = 1;
        db->reset_tx_timeout++;
    }


    /* DM9000 dynamic RESET check and do */
    if (db->device_wait_reset) {
        db->reset_counter++;
        db->device_wait_reset = 0;
        pDm9000Dev->trans_start = 0;
        PDM9000_Init(pDm9000Dev);
    }

    /* Auto Sense Media mode policy:
     * FastEthernet NIC: don't need to do anything.
     * Media Force mode: don't need to do anything.
     * HomeRun/LongRun NIC and AUTO_Mode:
     * INT_MII not link, select EXT_MII
     * EXT_MII not link, select INT_MII
     */
    if ( (db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_AUTO) ) {
        tmp_reg = ior(db, 0x01);        /* Got link status */
        if ( !(tmp_reg & 0x40) ) {         /* not link */
            db->reg0 ^= 0x80;
            iow(db, 0x00, db->reg0);
        }
    }

    /* Restore previous register address */
    IOSpaceWriteInt8( db->ioaddr, reg_save);
}

/*
Received a packet and pass to upper layer
*/
char rdptr[DM9000_PKT_MAX + 1];
void DM9000Receive(PDM9000  pThis)
{
    PDM9000 pDm9000Dev = pThis;
    board_info_t *db = (board_info_t *)pDm9000Dev->board;
    UInt8 rxbyte,  tmpbyte;
    UInt16 i, RxStatus, RxLen, GoodPacket, tmplen;
    UInt32 tmpdata;

    DM9000DBUG(1, "DM9000Receive()", 0);
    /* Check packet ready or not */
    do {
        tmpbyte = ior(db, 0xf0);        /* Dummy read */
        rxbyte = IOSpaceReadInt8(db->io_data);    /* Got most updated data */

        Dm_Delay(500);

        /* Status check: this byte must be 0 or 1 */
        if (rxbyte > DM9000_PKT_RDY) {
            iow(db, 0x05, 0x00);        /* Stop Device */
            iow(db, 0xfe, 0x80);        /* Stop Int32 request */
            db->device_wait_reset = TRUE;
            db->reset_rx_status++;
        }

        /* packet ready to receive check */
        if (rxbyte & DM9000_PKT_RDY) {
            /* A packet ready now  & Get status/length */
            GoodPacket = TRUE;
            IOSpaceWriteInt8( db->ioaddr, 0xf2);

	        RxStatus = 0;
	        RxLen    = 0;
            if (db->io_mode == DM9000_BYTE_MODE) { /* Byte mode */
                RxStatus = IOSpaceReadInt8(db->io_data) +
                           (IOSpaceReadInt8(db->io_data) << 8);
                RxLen    = IOSpaceReadInt8(db->io_data) +
                           (IOSpaceReadInt8(db->io_data) << 8);
            }
            else if (db->io_mode == DM9000_WORD_MODE) { /* Word mode */
                RxStatus = IOSpaceReadInt16(db->io_data);
                RxLen    = IOSpaceReadInt16(db->io_data);
            }
            else { /* DWord mode */
                tmpdata  = IOSpaceReadInt32(db->io_data);
                RxStatus = tmpdata;
                RxLen         = tmpdata >> 16;
            }

            /* Packet Status check */
            if (RxLen < 0x40) {
                GoodPacket = FALSE;
                db->runt_length_counter++;
            }

            if (RxLen > DM9000_PKT_MAX) {
                kprintf("<DM9000> RST: RX Len:%x\n", RxLen);
                db->device_wait_reset = TRUE;
                db->long_length_counter++;
            }

            if (RxStatus & 0xbf00) {
                GoodPacket = FALSE;
                if (RxStatus & 0x100)
                    db->stats.rx_fifo_errors++;
                if (RxStatus & 0x200)
                    db->stats.rx_crc_errors++;
                if (RxStatus & 0x8000)
                    db->stats.rx_length_errors++;
            }

            /* Move data from DM9000 */
            if (!db->device_wait_reset) {
                if ( GoodPacket ) {
                    /* Read received packet from RX SARM */
                    if (db->io_mode == DM9000_BYTE_MODE) { /* Byte mode */
                        for (i=0; i<RxLen; i++)
                            rdptr[i]=IOSpaceReadInt8(db->io_data);
                    }
                    else if (db->io_mode == DM9000_WORD_MODE) { /* Word mode */
                        tmplen = (RxLen + 1) / 2;
                        for (i = 0; i < tmplen; i++)
                            ((UInt16 *)rdptr)[i] = IOSpaceReadInt16(db->io_data);
                    }
                    else { /* DWord mode */
                        tmplen = (RxLen + 3) / 4;
                        for (i = 0; i < tmplen; i++) {
                            ((UInt32 *)rdptr)[i] = IOSpaceReadInt32(db->io_data);
                        }
                    }

                    /* Pass to upper layer */
                    db->stats.rx_packets++;
                    /* We assume that the header fits entirely in one mbuf. */

		            /* Packet is good, try to get a memory block */
		            unsigned long * p = (unsigned long *)malloc(RxLen);
                    if(p == NULL){
                        goto lab11;
                    }
                    Node* pnode = new Node(p, RxLen);
                    memcpy(p, rdptr, RxLen);

                    WaitResult wr;
                    pThis->pRmutex->Lock(&wr);
                    g_nodereadlist.InsertNode(pnode);

                    pThis->pRcondition->Pulse();

                    pThis->pRmutex->Unlock();
                }
                else {
                    /* Without buffer or error packet */
                    if (db->io_mode == DM9000_BYTE_MODE) { /* Byte mode */
                        for (i = 0; i < RxLen; i++)
                            IOSpaceReadInt8(db->io_data);
                    }
                    else if (db->io_mode == DM9000_WORD_MODE) {/* Word mode */
                        tmplen = (RxLen + 1) / 2;
                        for (i = 0; i < tmplen; i++)
                            IOSpaceReadInt16(db->io_data);
                    }
                    else { /* DWord mode */
                        tmplen = (RxLen + 3) / 4;
                        for (i = 0; i < tmplen; i++)
                            IOSpaceReadInt32(db->io_data);
                    }
                }
            }
        }
lab11:;
    }while(rxbyte == DM9000_PKT_RDY && !db->device_wait_reset);
}

/*
Read a word data from SROM
*/
UInt16 read_srom_word(board_info_t *db, int offset)
{
    iow(db, 0xc, offset);
    iow(db, 0xb, 0x4);
    Dm_Delay(2);
    iow(db, 0xb, 0x0);
    return (ior(db, 0xd) + (ior(db, 0xe) << 8) );
}

//void getmcaf(struct arpcom *ec,UInt8 * af);

/*
Set DM9000 multicast address
*/
void DM900_MultCast(PDM9000  pDm9000Dev)
{
    UInt8 af[8];
    int i = 0;
    board_info_t *db = (board_info_t *)pDm9000Dev->board;

    for (i = 0; i < 8; i++)
        iow(db, DR_MULTICASTBASE + i, af[i]);
}

void DM900_SetMac(PDM9000  pDm9000Dev)
{
    board_info_t *db = (board_info_t *)pDm9000Dev->board;

    iow(db, DR_PHY_ADDRBASE, macAddr[0]);
    iow(db, DR_PHY_ADDRBASE + 1, macAddr[1]);
    iow(db, DR_PHY_ADDRBASE + 2, macAddr[2]);
    iow(db, DR_PHY_ADDRBASE + 3, macAddr[3]);
    iow(db, DR_PHY_ADDRBASE + 4, macAddr[4]);
    iow(db, DR_PHY_ADDRBASE + 5, macAddr[5]);
}

/* Set PHY operationg mode
*/
void set_PHY_mode(board_info_t *db)
{
    UInt16 phy_reg4 = 0x01e1, phy_reg0=0x1000;

    if ( !(db->op_mode & DM9000_AUTO) ) {

        switch(db->op_mode) {
        case DM9000_10MHD:  phy_reg4 = 0x21;
            phy_reg0 = 0x0000;
            break;
        case DM9000_10MFD:  phy_reg4 = 0x41;
            phy_reg0 = 0x1100;
            break;
        case DM9000_100MHD: phy_reg4 = 0x81;
            phy_reg0 = 0x2000;
            break;
        case DM9000_100MFD: phy_reg4 = 0x101;
            phy_reg0 =0x3100;
        break;
        }
        phy_write(db, 0, phy_reg0);        /*  Tmp */
        phy_write(db, 4, phy_reg4);       /* Set PHY media mode */
    }
    else {
        phy_write(db, 0, 0x8000);        /*  Tmp */
    }

    iow(db, 0x1e, 0x01);   /* Let GPIO0 output */
    iow(db, 0x1f, 0x00);   /* Enable PHY */
}

/*
Init HomeRun DM9801
*/
void program_dm9801(board_info_t *db, UInt16 HPNA_rev)
{
    UInt16 reg16, reg17, reg24, reg25;

    if ( !nfloor )
        nfloor = DM9801_NOISE_FLOOR;

    reg16 = phy_read(db, 16);
    reg17 = phy_read(db, 17);
    reg24 = phy_read(db, 24);
    reg25 = phy_read(db, 25);

    switch(HPNA_rev) {
    case 0xb900: /* DM9801 E3 */
        reg16 |= 0x1000;
        reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xf000;
        break;
    case 0xb901: /* DM9801 E4 */
        reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xc200;
        reg17 = (reg17 & 0xfff0) + nfloor + 3;
        break;
    case 0xb902: /* DM9801 E5 */
    case 0xb903: /* DM9801 E6 */
    default:
        reg16 |= 0x1000;
        reg25 = ( (reg24 + nfloor - 3) & 0x00ff) | 0xc200;
        reg17 = (reg17 & 0xfff0) + nfloor;
    }

    phy_write(db, 16, reg16);
    phy_write(db, 17, reg17);
    phy_write(db, 25, reg25);
}

/*
Init LongRun DM9802
*/
void program_dm9802(board_info_t *db)
{
    UInt16 reg25;

    if ( !nfloor )
        nfloor = DM9802_NOISE_FLOOR;

    reg25 = phy_read(db, 25);
    reg25 = (reg25 & 0xff00) + nfloor;
    phy_write(db, 25, reg25);
}

/* Identify NIC type
*/
void identify_nic(board_info_t *db)
{
    UInt16 phy_reg3;

    iow(db, 0, DM9000_EXT_MII);
    phy_reg3 = phy_read(db, 3);

    switch(phy_reg3 & 0xfff0) {
    case 0xb900:
        if (phy_read(db, 31) == 0x4404) {
            db->nic_type =  HOMERUN_NIC;
            program_dm9801(db, phy_reg3);
        } else {
            db->nic_type = LONGRUN_NIC;
            program_dm9802(db);
        }
        break;

    default: db->nic_type = FASTETHER_NIC;
        break;

    }

    iow(db, 0, DM9000_INT_MII);
}

/*
Read a byte from I/O port
*/
UInt8 ior(board_info_t *db, int reg)
{
    IOSpaceWriteInt8( db->ioaddr, reg);
    return IOSpaceReadInt8(db->io_data);
}

/*
Write a byte to I/O port
*/
void iow(board_info_t *db, int reg, UInt8 value)
{
    IOSpaceWriteInt8( db->ioaddr, reg);
    IOSpaceWriteInt8(db->io_data, value);
}

/*
Read a word from phyxcer
*/
UInt16 phy_read(board_info_t *db, int reg)
{
    /* Fill the phyxcer register into REG_0C */
    iow(db, 0xc, DM9000_PHY | reg);

    iow(db, 0xb, 0xc);         /* Issue phyxcer read command */
    Dm_Delay(100);             /* Wait read complete */
    iow(db, 0xb, 0x0);         /* Clear phyxcer read command */

    /* The read data keeps on REG_0D & REG_0E */
    return ( ior(db, 0xe) << 8 ) | ior(db, 0xd);
}

/*
Write a word to phyxcer
*/
void phy_write(board_info_t *db, int reg, UInt16 value)
{
    /* Fill the phyxcer register into REG_0C */
    iow(db, 0xc, DM9000_PHY | reg);

    /* Fill the written data into REG_0D & REG_0E */
    iow(db, 0xd, (value & 0xff));
    iow(db, 0xe, ( (value >> 8) & 0xff));

    iow(db, 0xb, 0xa);/* Issue phyxcer write command */
    Dm_Delay(500);        /* Wait write complete */
    iow(db, 0xb, 0x0);/* Clear phyxcer write command */
}

